package com.kcht.itsed.mws.aleframework.specmanage;

import com.kcht.itsed.mws.IMwsXmlObjFactory;
import com.kcht.itsed.mws.MwsXmlObjFactory;
import com.kcht.itsed.mws.aleframework.intf.ISubscriptionSender;
import com.kcht.itsed.mws.alemodel.*;
import com.kcht.itsed.mws.alemodel.ECReportGroupListMemberExtension.FieldList;
import com.kcht.itsed.mws.alemodel.ECReports.Reports;
import com.kcht.itsed.mws.commmodel.CRAP;
import com.kcht.itsed.mws.commmodel.SubscribedReport;
import com.shhic.itsed.mws.deviceintf.AntennaProtocol;
import com.shhic.itsed.mws.deviceintf.RawTagOperationResult;
import com.shhic.itsed.mws.deviceintf.RawTagReadResult;
import com.shhic.itsed.mws.deviceintf.ReaderCommand;
import lombok.extern.slf4j.Slf4j;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import java.util.*;
import java.util.function.Function;

@Slf4j
public class ECSpecScheduleThread implements ISpecScheduleThread{
	
	private ISubscriptionSender subscriptionSender;
	private String ecName;
	private ECSpec ecSpec;
	private IBoundaryConditionScheduler boundaryCond;
	private Set<String> includedRapStrings = new HashSet<>();	//包含的所有CRAP元组，如果结果不属于此，那么可以直接扔掉
		//这个字符串的格式简化了CRAP格式：ID[阅读器ID]:Ant[天线号]:Protocol[协议]
	
	private boolean reportIfEmpty;	//是否发送空报告
	
	private Map<String,String> tmMapping = new HashMap<>();	//标签区规则的映射，key是TM原生表示（例如：2.6.0），value是EC引用的TMField规则名称

	private Map<String,String> lrMapping = new HashMap<>();	//逻辑阅读器映射，key是CRAP简化（同includedRapStrings), value逻辑阅读器名称
	

	private IMwsXmlObjFactory<ECReports> f = MwsXmlObjFactory.create(ECReports.class);
	
	private Function<Void,Void> scheduleBiz;	//调度业务。区分连续调度和非连续调度

	//当前周期的标签读取结果
	private Map<String,RawTagReadResult> tagsOfCurrentCycle = new HashMap<>();	//key是EPC（后续可能扩展成标识码）
	
	private boolean threadRunning = true; 
	private boolean isActive = false;
	
	public ECSpecScheduleThread(String ecName,ECSpec ecSpec,Set<TMFixedFieldSpec> relatedTMSpecs,Map<String,Set<CRAP>> relatedLRCraps,ISubscriptionSender subscriptionSender) {
		this.ecName = ecName;
		this.subscriptionSender = subscriptionSender;
		this.ecSpec = ecSpec;

		boundaryCond = generateBoundaryCond();
		
		
		reportIfEmpty = ecSpec.getReportSpecs().getReportSpec().get(0).isReportIfEmpty();
			//目前一个EC规则只含有一个报告规则
		
		//添加TM规则映射
		relatedTMSpecs.forEach(tmf->
			tmMapping.put(String.format("%d.%d.%d", tmf.getBank(),tmf.getLength(),tmf.getOffset()), 
					tmf.getFieldname())
		);
		
		//添加LR规则映射
		relatedLRCraps.entrySet().forEach(lrc->
			lrc.getValue().forEach(crap->
					lrMapping.put(String.format("ID[%s]:Ant[%s]:Protocol[%s]",
							crap.getReaderId(),crap.getAntenna(),crap.getProtocol()), lrc.getKey())
		));
	}
	
	
	private IBoundaryConditionScheduler generateBoundaryCond() {
		//目前只支持固定周期触发的边界条件
		long repeatPeriod = ecSpec.getBoundarySpec().getRepeatPeriod().getValue();
		long duration = ecSpec.getBoundarySpec().getDuration().getValue();

		IBoundaryConditionScheduler condScheduler = new FixedReportPeriodBoundaryCondition(repeatPeriod,duration);
		scheduleBiz = repeatPeriod != duration ? this::scheduleWithDiscontinuousBound : this::scheduleWithContinuousBound;
		return 	condScheduler;
	}


	@Override
	public void notifyReverseTagResult(RawTagOperationResult tagRes) {
		// 过滤标签
		if(!isActive)return;//非活动周期内上报，不管之
		log.info("处理标签读取的原始结果"+tagRes.getTagIdentifier());
		if(!(tagRes instanceof RawTagReadResult))return; //非标签读取结果，不管之
		RawTagReadResult tagReadRes = (RawTagReadResult)tagRes;
		if(filterEcSpec(tagReadRes)) {
			synchronized(tagsOfCurrentCycle) {
				String tagId = tagRes.getTagIdentifier();
				if(tagsOfCurrentCycle.containsKey(tagId)) {
					RawTagReadResult currentTag = tagsOfCurrentCycle.get(tagId);
					currentTag.setDiscoverCount(currentTag.getDiscoverCount() + tagReadRes.getDiscoverCount());
				}else {
					tagsOfCurrentCycle.put(tagId,tagReadRes);
				}
			}
		}
	}

	private boolean filterEcSpec(RawTagReadResult tagReadRes) {
		//验证这个标签读取结果属于本EC规则
		//过滤CRAP
		String tagRapStr = toRAPStr(tagReadRes.getReaderId(), tagReadRes.getAntennaNo(), tagReadRes.getTagProtocol());
		synchronized(includedRapStrings) {
			if(!includedRapStrings.contains(tagRapStr))return false;
		}
		
		//TODO 过滤标签区规则和读取结果。当前版本不支持读取区域拼接，只要阅读器符合，就可以了
		return true;
	}

	
	@Override
	public void stopThread() {
		threadRunning = false;
		boundaryCond.stopBoundaryCondition();
	}
	
	
	
	
	/**
	 * 不连续边界条件调度
	 * 不连续的 边界条件，指的是在触发结束条件和下一周期开始条件满足之间，存在空档
	 * @param noParam
	 * @return
 
	 */
	private Void scheduleWithDiscontinuousBound(Void noParam) {
		if(boundaryCond.initiationConditionSatisfied()) {
			synchronized(tagsOfCurrentCycle) {
				isActive = true;
				tagsOfCurrentCycle.clear();
			}
		}
		
		if(boundaryCond.terminationConditionSatisfied()) {
			isActive = false;
			Set<RawTagReadResult> reportTagSet = new HashSet<>();
			synchronized (tagsOfCurrentCycle) {
				if(!reportIfEmpty && tagsOfCurrentCycle.isEmpty())return null;
				
				reportTagSet.addAll(tagsOfCurrentCycle.values());
				for (RawTagReadResult tagReadResult : reportTagSet) {
					log.info("上报给所有订阅者的标签--->"+"EPC码:"+tagReadResult.getTagIdentifier()
							+",IP:"+tagReadResult.getReaderIp()
							+",天线号:"+tagReadResult.getAntennaNo()
							+",时间:"+new Date(tagReadResult.getDiscoverTimestamp()));
				}
				tagsOfCurrentCycle.clear();
			}
			SubscribedReport report = generateReport(reportTagSet);
			subscriptionSender.send(report);	//TODO 检查如果是空报告，是否还要发送之
		}
		return null;
	}
	
	/**
	 * 连续边界条件的调度
	 * @param noParam
	 * @return
	 */
	private Void scheduleWithContinuousBound(Void noParam) {
		isActive = true;
		if(boundaryCond.initiationConditionSatisfied()) {
			synchronized(tagsOfCurrentCycle) {
				Set<RawTagReadResult> reportTagSet = new HashSet<>();

				if(!reportIfEmpty && tagsOfCurrentCycle.isEmpty())return null;

				reportTagSet.addAll(tagsOfCurrentCycle.values());
				for (RawTagReadResult tagReadResult : reportTagSet) {
					log.info("上报给所有订阅者的标签--->"+"EPC码:"+tagReadResult.getTagIdentifier()
							+",IP:"+tagReadResult.getReaderIp()
							+",天线号:"+tagReadResult.getAntennaNo()
							+",时间:"+new Date(tagReadResult.getDiscoverTimestamp()));
				}
				SubscribedReport report = generateReport(reportTagSet);
				subscriptionSender.send(report);	
				tagsOfCurrentCycle.clear();

			}
		}
		return null;

	}
	
	
	@Override
	public void run() {
		while(threadRunning) {
			try {
				scheduleBiz.apply(null);
			}catch(Exception e) {
				log.error("",e);
			}
		}
	}
	
	private static XMLGregorianCalendar dateToXmlDate(Date date){  
        Calendar cal = Calendar.getInstance();  
        cal.setTime(date);  
        DatatypeFactory dtf = null;  
         try {  
            dtf = DatatypeFactory.newInstance();  
        } catch (DatatypeConfigurationException e) {  
        }  
        XMLGregorianCalendar dateType = dtf.newXMLGregorianCalendar();  
        dateType.setYear(cal.get(Calendar.YEAR));  
        //由于Calendar.MONTH取值范围为0~11,需要加1  
        dateType.setMonth(cal.get(Calendar.MONTH)+1);  
        dateType.setDay(cal.get(Calendar.DAY_OF_MONTH));  
        dateType.setHour(cal.get(Calendar.HOUR_OF_DAY));  
        dateType.setMinute(cal.get(Calendar.MINUTE));  
        dateType.setSecond(cal.get(Calendar.SECOND));  
        return dateType;  
    }   	

	
	//处理EPC编码
	private void processEpcEncode(ECReportGroupListMember member, String inventoryResult) {
		ECReportOutputSpec output = ecSpec.getReportSpecs().getReportSpec().get(0).getOutput();
		
		if(output.isIncludeRawHex()) {
			EPC epc = new EPC();
			epc.setValue(inventoryResult);
			member.setRawHex(epc);
		}

		/*原始标签值改为10进制*/
		if(output.isIncludeRawDecimal()) {
			EPC epc = new EPC();
			epc.setValue(Long.toString(Long.parseLong(inventoryResult, 16)));
			member.setRawDecimal(epc);
		}
		
		if(output.isIncludeTag()) {
			//TODO 处理TAG编码

		}
	}
	
	
	
	private ECReportGroupListMember toMember(RawTagReadResult tag) {
		ECReportGroupListMember member = new ECReportGroupListMember();
		
		
		//处理EPC编码
		processEpcEncode(member,tag.getInventoryResult());

		ECReportGroupListMemberExtension memberExtension = new ECReportGroupListMemberExtension();

		// 从EC规则中抓Field读取结果出来
		if(tag.getTagFieldsReadResult() != null && !tag.getTagFieldsReadResult().isEmpty()) 
			memberExtension.setFieldList(new FieldList());

		tag.getTagFieldsReadResult().forEach(rtf->{
			String oriFieldDecs = rtf.toFieldOriDesc();
			if(tmMapping.containsKey(oriFieldDecs)) {
				ECReportMemberField ecField = new ECReportMemberField();
				ecField.setName(tmMapping.get(oriFieldDecs));
				ecField.setValue(rtf.getFieldValue());
				//TODO 处理标签区Field的特殊编码，目前只是原始HEX
				memberExtension.getFieldList().getField().add(ecField);
			}
		});
		
		memberExtension.getOtherAttributes().put(new QName("discoverLocation"), 
				String.format("LRName[%s]:Class[%s]:ID[%s]:IP[%s]:Ant[%s]:Protocol[%s]",
						lrMapping.get(toRAPStr(tag.getReaderId(), tag.getAntennaNo(), tag.getTagProtocol())),
						tag.getReaderClass(),tag.getReaderId(),tag.getReaderIp(),tag.getAntennaNo(),tag.getTagProtocol())
				);
		memberExtension.getOtherAttributes().put(new QName("discoverTimestamp"), Long.toString(tag.getDiscoverTimestamp()));
		memberExtension.getOtherAttributes().put(new QName("sightingCount"),Long.toString(tag.getDiscoverCount()));
		
		member.setExtension(memberExtension);
		
		return member;

	}

	private ECReports generateEcReports(Set<RawTagReadResult> reportTagSet) {
		ECReports report = new ECReports();
		report.setSpecName(ecName);
		report.setALEID("TJKCHT");
		report.setDate(dateToXmlDate(new Date()));
		
		
		Reports reports = new Reports();
		ECReport ecReport = new ECReport();
		
		//目前一个EC规则只有一个Report，一个group
		ecReport.setReportName(ecSpec.getReportSpecs().getReportSpec().get(0).getReportName());
		ECReportGroup ecGroup1 = new ECReportGroup();
		ECReportGroupList gl = new ECReportGroupList();
		
		reportTagSet.stream().map(this::toMember).forEach(gl.getMember()::add);
		ecGroup1.setGroupList(gl);
		ecReport.getGroup().add(ecGroup1);
		
		if(ecSpec.getReportSpecs().getReportSpec().get(0).getOutput().isIncludeCount()) {
			//添加标签计数
			ECReportGroupCount countValue = new ECReportGroupCount();
			countValue.setCount(ecGroup1.getGroupList().getMember().size());
			ecGroup1.setGroupCount(countValue);
		}
		
		reports.getReport().add(ecReport);
		report.setReports(reports);
		
		return report;
	}
	

	private SubscribedReport generateReport(Set<RawTagReadResult> reportTagSet) {
		//生成报告
		SubscribedReport report = new SubscribedReport();
		report.setSpecType("EC");
		report.setSpecName(ecName);
		report.setReportXml(f.toXml(generateEcReports(reportTagSet)));
		return report ;
	}


	@Override
	public void registerRelatedReaderCommands(Set<ReaderCommand> allReaderCommands) {
		synchronized (includedRapStrings) {
			includedRapStrings.clear();
			allReaderCommands.forEach(rc->{
				String readerId = rc.getReaderId();
				for(AntennaProtocol ap:rc.getAntennaProtocolSet()) {
					String ant= Integer.toString(ap.getAntennaNo());
					for(String protocol:ap.getProtocols()) {
						includedRapStrings.add(toRAPStr(readerId,ant,protocol));
					}
				}
			});
		}
	}
	
	private String toRAPStr(String readerId,String ant,String protocol) {
		return String.format("ID[%s]:Ant[%s]:Protocol[%s]",readerId,ant,protocol);
	}
	

}
